package com.kaigejava.utils;

/**
 * Created by kaigejava on 2019/6/16.
 */

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import com.kaigejava.myannotation.ExcelAnnotation;
import com.sun.org.apache.xpath.internal.operations.Bool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
 * 注解Excel导出工具类
 */
@Slf4j
public class AnnotationExcelUtils<T> {
    Class<T> clazz;

    public AnnotationExcelUtils(Class<T> clazz) {
        this.clazz = clazz;
    }


    /**
     * 对list数据源将其里面的数据导入到excel表单 --增强写法
     * 为了不影响之前功能新增加此方法的
     *
     * @param sheetName
     *            工作表的名称
     * @param output
     *            每个sheet中数据的行数,此数值必须小于65536
     * @param output
     *            java输出流
     */
    @SuppressWarnings("unchecked")
    public boolean exportExcelEnhance(List<T> list, String sheetName,
                                      OutputStream output) {
        //此处 对类型进行转换
        List<T> ilist = new ArrayList<T>();
        for (T t : list) {
            ilist.add(t);
        }
        List<T>[] lists = new ArrayList[1];
        lists[0] = ilist;

        String[] sheetNames = new String[1];
        sheetNames[0] = sheetName;
        return  exportExcelEnhanceExe(lists, sheetNames, output);
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单--增强写法
     *
     * @param sheetNames
     *            工作表的名称
     * @param output
     *            java输出流
     */
    public boolean exportExcelEnhanceExe(List<T> lists[], String sheetNames[],
                                         OutputStream output)  {
        if (lists.length != sheetNames.length) {
            System.out.println("数组长度不一致");
            return false;
        }

        HSSFWorkbook workbook = new HSSFWorkbook();// 产生工作薄对象

        for (int ii = 0; ii < lists.length; ii++) {
            List<T> list = lists[ii];
            String sheetName = sheetNames[ii];

            List<Field> fields = getMappedFiled(clazz, null);

            HSSFSheet sheet = workbook.createSheet();// 产生工作表对象

            workbook.setSheetName(ii, sheetName);

            HSSFRow row;
            HSSFCell cell;// 产生单元格
            HSSFCellStyle style = workbook.createCellStyle();
            style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
            style.setFillBackgroundColor(HSSFColor.GREY_40_PERCENT.index);
            row = sheet.createRow(0);// 产生一行
            // 写入各个字段的列头名称
            for (int i = 0; i < fields.size(); i++) {
                Field field = fields.get(i);
                ExcelAnnotation attr = field
                        .getAnnotation(ExcelAnnotation.class);
                int col = getExcelCol(attr.colum());// 获得列号
                cell = row.createCell(col);// 创建列
                cell.setCellType(HSSFCell.CELL_TYPE_STRING);// 设置列中写入内容为String类型
                cell.setCellValue(attr.ZHName());// 写入列名

                // 如果设置了提示信息则鼠标放上去提示.
                if (!attr.prompt().trim().equals("")) {
                    setHSSFPrompt(sheet, "", attr.prompt(), 1, 100, col, col);// 这里默认设了2-101列提示.
                }
                // 如果设置了combo属性则本列只能选择不能输入
                if (attr.combo().length > 0) {
                    setHSSFValidation(sheet, attr.combo(), 1, 100, col, col);// 这里默认设了2-101列只能选择不能输入.
                }
                cell.setCellStyle(style);
            }

            int startNo = 0;
            int endNo = list.size();
            // 写入各条记录,每条记录对应excel表中的一行
            for (int i = startNo; i < endNo; i++) {
                row = sheet.createRow(i + 1 - startNo);
                T vo = (T) list.get(i); // 得到导出对象.
                for (int j = 0; j < fields.size(); j++) {
                    Field field = fields.get(j);// 获得field.
                    field.setAccessible(true);// 设置实体类私有属性可访问
                    ExcelAnnotation attr = field
                            .getAnnotation(ExcelAnnotation.class);
                    try {
                        // 根据ExcelVOAttribute中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
                        if (attr.isExport()) {
                            cell = row.createCell(getExcelCol(attr.colum()));// 创建cell
                            cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                            if (!attr.isFormat() && !attr.isDateFormat()) {
                                    /** 优化初级版本
                                    Class clazz = attr.EnumClazz();
                                    if(!"java.lang.String".equals(clazz.getName())){ //需要进行转换
                                        Method method = clazz.getMethod(attr.EnumMethodName(),attr.methodArgClass());
                                        Object obj = method.invoke(clazz,field.get(vo));
                                        if(null != obj ){
                                            cell.setCellValue( obj.toString());
                                        }else{
                                            cell.setCellValue( "");
                                        }
                                    }else{
                                        cell.setCellValue(field.get(vo) == null ? ""
                                                : attr.pre()+String.valueOf(field.get(vo))+attr.suffix());
                                    }
                                     **/
                                    //优化plush版本
                                if(attr.EnumCode2Description().length>0){
                                    Class<?> aClass = attr.EnumCode2Description()[0].clazz();
                                    Method method =  aClass.getMethod(attr.EnumCode2Description()[0].methodName(),attr.EnumCode2Description()[0].methodArgClass());
                                    Object obj = method.invoke(aClass,field.get(vo));
                                    if(null != obj ){
                                        cell.setCellValue( obj.toString());
                                    }else{
                                        cell.setCellValue( "");
                                    }
                                }else{
                                    if(attr.myDateFormat2Pattern().length>0){
                                        Boolean isDateFormate = attr.myDateFormat2Pattern()[0].isDateFormate();
                                        if(isDateFormate){
                                            String sdfStr = attr.myDateFormat2Pattern()[0].pattern();
                                            SimpleDateFormat sdf = new SimpleDateFormat(sdfStr);
                                            if(null != field.get(vo)){
                                                String dateStr = sdf.format(field.get(vo));
                                                cell.setCellValue(dateStr);
                                            }else{
                                                log.info("==>在导出的时候，格式化日期获取到的日期为空。对应字段名称为:{}",field.getName());
                                                cell.setCellValue("");
                                            }
                                        }
                                    }else{
                                        cell.setCellValue(field.get(vo) == null ? ""
                                                : attr.pre()+String.valueOf(field.get(vo))+attr.suffix());
                                    }
                                }
                            } else if (!attr.isDateFormat()&&null != attr.value() && null != field.get(vo)){
                                String[] dict = attr.value().split(",");
                                for(String item : dict) {
                                    String[] s = item.split(":");
                                    if (s[0].equals(field.get(vo).toString())) {
                                        cell.setCellValue(s[1]);
                                        break;
                                    }
                                }
                            }
                          /*  初级版本对日期格式化
                          else if(attr.isDateFormat()){



                              String sdfStr = attr.dateFormatSdf();
                                if(StringUtils.isBlank(sdfStr)){
                                    sdfStr = "yyyy-MM-dd";
                                }
                                SimpleDateFormat sdf = new SimpleDateFormat(sdfStr);
                                if(null != field.get(vo)){
                                    String dateStr = sdf.format(field.get(vo));
                                    cell.setCellValue(dateStr);
                                }else{
                                    log.info("==>在导出的时候，格式化日期获取到的日期为空。对应字段名称为:{}",field.getName());
                                    cell.setCellValue("");
                                }
                            }else {
                                cell.setCellValue("");
                            }*/
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        try {
            output.flush();
            workbook.write(output);
            output.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Output is closed ");
            return false;
        }

    }

    /**
     * 得到实体类所有通过注解映射了数据表的字段
     *
     * @param clazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    private List<Field> getMappedFiled(Class clazz, List<Field> fields) {
        if (fields == null) {
            fields = new ArrayList<Field>();
        }

        Field[] allFields = clazz.getDeclaredFields();// 得到所有定义字段
        // 得到所有field并存放到一个list中.
        for (Field field : allFields) {
            if (field.isAnnotationPresent(ExcelAnnotation.class)) {
                fields.add(field);
            }
        }
        if (clazz.getSuperclass() != null
                && !clazz.getSuperclass().equals(Object.class)) {
            getMappedFiled(clazz.getSuperclass(), fields);
        }

        return fields;
    }


    /**
     * 将EXCEL中A,B,C,D,E列映射成0,1,2,3
     *
     * @param col
     */
    public static int getExcelCol(String col) {
        col = col.toUpperCase();
        // 从-1开始计算,字母重1开始运算。这种总数下来算数正好相同。
        int count = -1;
        char[] cs = col.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            count += (cs[i] - 64) * Math.pow(26, cs.length - 1 - i);
        }
        return count;
    }

    /**
     * 设置单元格上提示
     *
     * @param sheet
     *            要设置的sheet.
     * @param promptTitle
     *            标题
     * @param promptContent
     *            内容
     * @param firstRow
     *            开始行
     * @param endRow
     *            结束行
     * @param firstCol
     *            开始列
     * @param endCol
     *            结束列
     * @return 设置好的sheet.
     */
    public static HSSFSheet setHSSFPrompt(HSSFSheet sheet, String promptTitle,
                                          String promptContent, int firstRow, int endRow, int firstCol,
                                          int endCol) {
        // 构造constraint对象
        DVConstraint constraint = DVConstraint
                .createCustomFormulaConstraint("DD1");
        // 四个参数分别是：起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow,
                endRow, firstCol, endCol);
        // 数据有效性对象
        HSSFDataValidation data_validation_view = new HSSFDataValidation(
                regions, constraint);
        data_validation_view.createPromptBox(promptTitle, promptContent);
        sheet.addValidationData(data_validation_view);
        return sheet;
    }


    /**
     * 设置某些列的值只能输入预制的数据,显示下拉框.
     *
     * @param sheet
     *            要设置的sheet.
     * @param textlist
     *            下拉框显示的内容
     * @param firstRow
     *            开始行
     * @param endRow
     *            结束行
     * @param firstCol
     *            开始列
     * @param endCol
     *            结束列
     * @return 设置好的sheet.
     */
    public static HSSFSheet setHSSFValidation(HSSFSheet sheet,
                                              String[] textlist, int firstRow, int endRow, int firstCol,
                                              int endCol) {
        // 加载下拉列表内容
        DVConstraint constraint = DVConstraint
                .createExplicitListConstraint(textlist);
        // 设置数据有效性加载在哪个单元格上,四个参数分别是：起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow,
                endRow, firstCol, endCol);
        // 数据有效性对象
        HSSFDataValidation data_validation_list = new HSSFDataValidation(
                regions, constraint);
        sheet.addValidationData(data_validation_list);
        return sheet;
    }

}
